#include "PLVertex.h"
#include "PLVec2.h"

using std::map;
using std::set;

PLuint PLVertex::mNumVertexes = 0;

const PLuint R = 2U;
const PLuint G = 1U;
const PLuint B = 0U;

/**
  *	Constructs a new PLVertex object with the given arguments
  *
  * @param vertexIndex vertex index
  * @param texcoordIndex texcoord index
  * @param textureID unique texture ID
  */
PLVertex::PLVertex( PLuint vertexIndex, PLuint texcoordIndex, PLuint textureID )
{
	mVertexIndex = vertexIndex;
	mTexcoordIndex = texcoordIndex;
	mTextureID = textureID;

	mLevellingBGR[B] = 0.0;
	mLevellingBGR[G] = 0.0;
	mLevellingBGR[R] = 0.0;

	mOriginalBGR[B] = 0.0;
	mOriginalBGR[G] = 0.0;
	mOriginalBGR[R] = 0.0;
	
	mAdjacentVertexes = 0;
	mBlobIDToNormalMap = 0;

	mNumVertexes++;
}

/**
  *	Destroys the PLVertex object
  */
PLVertex::~PLVertex( void )
{
	if (mAdjacentVertexes) delete mAdjacentVertexes;
	if (mBlobIDToNormalMap) delete mBlobIDToNormalMap;

	mNumVertexes--;
}

/**
  *	Adds an adjacent PLVertex object. The object is not copied, just the pointer
  * is stored.
  *
  * @param adjacentVertex pointer to the adjacent PLVertex object
  */
void PLVertex::AddAdjacentVertex( PLVertex *adjacentVertex )
{
	if (!mAdjacentVertexes)
		mAdjacentVertexes = new set<PLVertex *>();

	mAdjacentVertexes->insert( adjacentVertex );
}

/**
  *	Returns a const pointer to the set with the pointers to the adjacent PLVertex objects.
  * The return value can be 0 when no adjacent PLVertex object exists.
  *
  * @return const pointer to the set with the pointers to the adjacent PLVertex objects
  */
const set<PLVertex *> *PLVertex::GetAdjacentVertexes( void ) const
{
	return mAdjacentVertexes;
}

/**
  *	Returns a const pointer to the normal vector for the given PLBlob ID or
  * 0, when no such normal exists.
  *
  * @param blobID PLBlob ID
  * @return const pointer to the normal vector for the given PLBlob ID
  */
const PLVec2d *PLVertex::GetNormal( const PLuint &blobID ) const
{
	std::map<PLuint, PLVec2d>::iterator normal_it = mBlobIDToNormalMap->find( blobID );
	
	if (normal_it == mBlobIDToNormalMap->end())
		return 0;

	return &normal_it->second;
}

/**
  *	Returns the levelling value for the red channel
  *
  * @return levelling value for the red channel
  */
const double &PLVertex::GetLevellingR( void ) const
{
	return mLevellingBGR[R];
}

/**
  *	Returns the levelling value for the green channel
  *
  * @return levelling value for the green channel
  */
const double &PLVertex::GetLevellingG( void ) const
{
	return mLevellingBGR[G];
}

/**
  *	Returns the levelling value for the blue channel
  *
  * @return levelling value for the blue channel
  */
const double &PLVertex::GetLevellingB( void ) const
{
	return mLevellingBGR[B];
}

/**
  *	Returns a const pointer to the double-array with the levelling values in the order BGR
  *
  * @return const pointer to the double-array with the levelling values
  */
const double *PLVertex::GetLevellingBGR( void ) const
{
	return mLevellingBGR;
}

/**
  *	Returns the original value for the red channel
  *
  * @return original value for the red channel
  */
const double &PLVertex::GetOriginalR( void ) const
{
	return mOriginalBGR[R];
}

/**
  *	Returns the original value for the green channel
  *
  * @return original value for the green channel
  */
const double &PLVertex::GetOriginalG( void ) const
{
	return mOriginalBGR[G];
}

/**
  *	Returns the original value for the blue channel
  *
  * @return original value for the blue channel
  */
const double &PLVertex::GetOriginalB( void ) const
{
	return mOriginalBGR[B];
}

/**
  *	Returns a const pointer to the double-array with the original color values in the order BGR
  *
  * @return const pointer to the double-array with the original color values
  */
const double *PLVertex::GetOriginalBGR( void ) const
{
	return mOriginalBGR;
}

/**
  *	Returns the number of currently instantiated PLVertex objects
  *
  * @return number of currently instantiated PLVertex objects
  */
const PLuint &PLVertex::GetNumVertexes( void )
{
	return mNumVertexes;
}

/**
  *	Sets the levelling value for the red channel
  *
  * @param levellingR levelling value for the red channel
  */
void PLVertex::SetLevellingR( const double &levellingR )
{
	mLevellingBGR[R] = levellingR;
}

/**
  *	Sets the levelling value for the green channel
  *
  * @param levellingG levelling value for the green channel
  */
void PLVertex::SetLevellingG( const double &levellingG )
{
	mLevellingBGR[G] = levellingG;
}

/**
  *	Sets the levelling value for the blue channel
  *
  * @param levellingB levelling value for the blue channel
  */
void PLVertex::SetLevellingB( const double &levellingB )
{
	mLevellingBGR[B] = levellingB;
}

/**
  *	Sets the levelling values for all three channels
  *
  * @param bgr const pointer to the double-array with the levelling values with the order BGR
  */
void PLVertex::SetLevellingColorBGR( const double *bgr )
{
	memcpy( mLevellingBGR, bgr, 3 * sizeof(double) );
}

/**
  *	Sets the original color values for all three channels
  *
  * @param bgr const pointer to the double-array with the original color values with the order BGR
  */
void PLVertex::SetOriginalColorBGR( const double *bgr )
{
	memcpy( mOriginalBGR, bgr, 3 * sizeof(double) );
}

/**
  *	Sets the normal of the PLVertex object for a particular PLBlob ID
  *
  * @param normal normal of the PLVertex object
  * @param blobID ID of the corresponding PLBlob object
  */
void PLVertex::SetNormal( const PLVec2d &normal, const PLuint &blobID )
{
	if (!mBlobIDToNormalMap) mBlobIDToNormalMap = new map<PLuint, PLVec2d>();
	
	(*mBlobIDToNormalMap)[blobID] = normal;
}

/**
  *	Returns the texcoord index
  *
  * @return texcoord index
  */
const PLuint &PLVertex::GetTexcoordIndex( void ) const
{
	return mTexcoordIndex;
}

/**
  *	Returns the texture ID
  *
  * @return texture ID
  */
const PLuint &PLVertex::GetTextureID( void ) const
{
	return mTextureID;
}

/**
  *	Returns the vertex index
  *
  * @return vertex index
  */
const PLuint &PLVertex::GetVertexIndex( void ) const
{
	return mVertexIndex;
}

/**
  *	Overloaded ()-operator for the PLVertexCompare class. At first the texture IDs
  * are compared, then the vertex indices, then the texcoord indices. Only when
  * one value of first is smaller then the corresponding value of second, true is returned,
  * false otherwise.
  *
  * @param first const reference to the first PLVertex object in the comparison
  * @param second const reference to the second PLVertex object in the comparison
  * @return only when one value of first is smaller then the corresponding value of second, true is returned, false otherwise.
  */
bool PLVertex::PLVertexCompare::operator()(const PLVertex &first, const PLVertex &second) const
{
	PLVertex::PLVertexPCompare compare;
	return compare( &first, &second );
}

/**
  *	Overloaded ()-operator for the PLVertexPCompare class. At first the texture IDs
  * are compared, then the vertex indices, then the texcoord indices. Only when
  * one value of first is smaller then the corresponding value of second, true is returned,
  * false otherwise.
  *
  * @param first const pointer to the first PLVertex object in the comparison
  * @param second const pointer to the second PLVertex object in the comparison
  * @return only when one value of first is smaller then the corresponding value of second, true is returned, false otherwise.
  */
bool PLVertex::PLVertexPCompare::operator()(const PLVertex *first, const PLVertex *second) const
{
	if (first->mTextureID < second->mTextureID)
	{
		return true;
	}
	else if (first->mTextureID > second->mTextureID)
	{
		return false;
	}
	else
	{
		if (first->mVertexIndex < second->mVertexIndex)
		{
			return true;
		}
		else if (first->mVertexIndex > second->mVertexIndex)
		{
			return false;
		}
		else
		{
			if (first->mTexcoordIndex < second->mTexcoordIndex)
			{
				return true;
			}
			
			return false;
		}
	}
}